package dryven.codegen.asm;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import dryven.codegen.asm.typeinfo.ClassTypeInfo;
import dryven.codegen.asm.typeinfo.TypeInfo;

public class Literal implements ReadableSymbol {

	private Object value;
	private TypeInfo type;
	private MethodBuilder mb;
	private MethodVisitor mv;
	
	private Literal(Object v, Class<?> type, MethodBuilder builder) {
		value = v;
		this.type = new ClassTypeInfo(type);
		mb = builder;
		mv = mb.getMethodVisitor();
	}
	
	public Literal(boolean v, MethodBuilder builder) {
		this(v,boolean.class,builder);
	}
	public Literal(byte v, MethodBuilder builder) {
		this((int)v,byte.class,builder);
	}
	public Literal(char v, MethodBuilder builder) {
		this((int)v,char.class,builder);
	}
	public Literal(short v, MethodBuilder builder) {
		this((int)v,short.class,builder);
	}
	public Literal(int v, MethodBuilder builder) {
		this(v,int.class,builder);
	}
	public Literal(long v, MethodBuilder builder) {
		this(v,long.class,builder);
	}
	public Literal(float v, MethodBuilder builder) {
		this(v,float.class,builder);
	}
	public Literal(double v, MethodBuilder builder) {
		this(v,double.class,builder);
	}
	public Literal(String v, MethodBuilder builder) {
		this(v,String.class,builder);
	}
	//null
	public Literal(TypeInfo ti, MethodBuilder builder) throws ClassNotFoundException {
		this(null,Class.forName(ti.getTypeName()),builder);
	}
	public Literal(Class<?> v, MethodBuilder builder) {
		this(v,Class.class,builder);
	}
	@Override
	public TempValue read() {
		if(type.isBoolean()) {
			Boolean b = (Boolean)value;
			if(b.booleanValue()) {
				mv.visitInsn(Opcodes.ICONST_1);
			} else {
				mv.visitInsn(Opcodes.ICONST_0);
			}
		}
		if(type.isInt()) {
			mv.visitLdcInsn(((Integer)value).intValue());
		} else if(type.isByte() || type.isChar()) {
			mv.visitIntInsn(Opcodes.BIPUSH, ((Integer)value).intValue());
		} else if(type.isShort()) {
			mv.visitIntInsn(Opcodes.SIPUSH, ((Integer)value).intValue());
		} else if(type.isFloat()) {
			mv.visitLdcInsn((Float)value);
		} else if(type.isFloat()) {
			mv.visitLdcInsn((Float)value);
		} else if(type.isLong()) {
			mv.visitLdcInsn((Long)value);
		} else if(type.isDouble()) {
			mv.visitLdcInsn((Double)value);
		} else if(type.isReference()){
			if(value==null) {
				mv.visitInsn(Opcodes.ACONST_NULL);
			}
			else if(value instanceof String) {
				mv.visitLdcInsn((String)value);
			}
			else if(value instanceof Class<?>) {
				Class<?> clazz = (Class<?>)value;
				TypeInfo ti = new ClassTypeInfo(clazz);
				if(clazz.isPrimitive()) {
					Class autoBoxClass = null;
					if(clazz.equals(boolean.class)) {
						autoBoxClass = Boolean.class;
					} else if(clazz.equals(byte.class)) {
						autoBoxClass = Byte.class;
					} else if(clazz.equals(char.class)) {
						autoBoxClass = Character.class;
					} else if(clazz.equals(short.class)) {
						autoBoxClass = Short.class;
					} else if(clazz.equals(int.class)) {
						autoBoxClass = Integer.class;
					} else if(clazz.equals(long.class)) {
						autoBoxClass = Long.class;
					} else if(clazz.equals(float.class)) {
						autoBoxClass = Float.class;
					} else if(clazz.equals(double.class)) {
						autoBoxClass = Double.class;
					}
					mv.visitFieldInsn(Opcodes.GETSTATIC, new ClassTypeInfo(autoBoxClass).getInternalClassName(), "TYPE", new ClassTypeInfo(Class.class).getTypeDescriptor());
				} else {
					mv.visitLdcInsn(Type.getType(ti.getTypeDescriptor()));
				}
			}
		}
		return new TempValue(type,mb);
	}
	
	@Override
	public TypeInfo getTypeInfo() {
		return type;
	}
}
